package com.hopes.bizz.close.statistic.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.hopes.bizz.close.common.constant.ParkCloseProperty;
import com.hopes.bizz.close.common.constant.VisitorDicConstant;
import com.hopes.bizz.close.common.constant.enums.ParkCloseSysDictEnum;
import com.hopes.bizz.close.statistic.service.ClosedInOutStatisticService;
import com.hopes.bizz.close.statistic.vo.AccessInOutChartVO;
import com.hopes.bizz.close.statistic.vo.AccessInOutVO;
import com.hopes.bizz.close.statistic.vo.TypeNumVO;
import com.hopes.bizz.close.visitor.entity.ClosedPersonRecordEntity;
import com.hopes.bizz.close.visitor.service.ClosedPersonRecordService;
import com.hopes.bizz.common.core.constant.CommonEnum;
import com.hopes.bizz.common.core.util.DateUtils;
import com.hopes.boss.admin.api.entity.SysDictItem;
import com.hopes.boss.admin.api.resolver.DictResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author yumr
 * @ProjectName bizz
 * @Description 出入园统计分析
 * @time 2024/1/30 10:42
 */
@Service
@RequiredArgsConstructor
public class ClosedInOutStatisticServiceImpl implements ClosedInOutStatisticService {

    private final ParkCloseProperty parkCloseProperty;
    private final ClosedPersonRecordService closedPersonRecordService;


    @Override
    public AccessInOutChartVO querySingleAccessPersonChart(Long accessId, String beginTimeStr, String endTimeStr) {
        final Date endTime = DateUtils.parseDate(CommonEnum.StartEndEnum.END, endTimeStr);
        final Date beginTime = DateUtils.parseDate(CommonEnum.StartEndEnum.START, beginTimeStr);
        int calendar = Calendar.HOUR_OF_DAY;
        Function<Date, String> timeHandleMapper = DateUtils::limitHour;
        if (DateUtils.getIntervalDays(beginTime, endTime) > parkCloseProperty.getGis().getChartHourToDay()) {
            calendar = Calendar.DAY_OF_YEAR;
            timeHandleMapper = DateUtils::limitDay;
        }

        final List<AccessInOutVO> enterList = new ArrayList<>();
        final List<AccessInOutVO> outerList = new ArrayList<>();

        final List<String> indexList = handlerDateTime(beginTime, endTime, timeHandleMapper, calendar, 1);
        // 1、进出园区的人员类型
        final List<SysDictItem> personRecordTypeList = DictResolver.getItemsByType(ParkCloseSysDictEnum.close_person_type);
        final LambdaQueryWrapper<ClosedPersonRecordEntity> personRecordWrapper = buildQueryWrapper(ClosedPersonRecordEntity::getInOutTime, beginTime, endTime).eq(ClosedPersonRecordEntity::getAccessId, accessId);
        final Map<Integer, List<ClosedPersonRecordEntity>> directionListMap = closedPersonRecordService.list(personRecordWrapper).stream().filter(e -> ObjUtil.isNotNull(e.getDirection())).collect(Collectors.groupingBy(ClosedPersonRecordEntity::getDirection));
        final List<ClosedPersonRecordEntity> enterRecordList = directionListMap.get(VisitorDicConstant.CLOSE_VISITOR_DIRECTION_IN);
        final List<ClosedPersonRecordEntity> outerRecordList = directionListMap.get(VisitorDicConstant.CLOSE_VISITOR_DIRECTION_OUT);
        for (String index : indexList) {
            enterList.add(buildAccessInOutVo(index, personRecordTypeList, enterRecordList, timeHandleMapper));
            outerList.add(buildAccessInOutVo(index, personRecordTypeList, outerRecordList, timeHandleMapper));
        }
        return new AccessInOutChartVO(enterList, outerList, personRecordTypeList);
    }

    private AccessInOutVO buildAccessInOutVo(String index, List<SysDictItem> personRecordTypeList, List<ClosedPersonRecordEntity> recordList, Function<Date, String> timeHandleMapper) {
        if (CollUtil.isEmpty(personRecordTypeList)) {
            return new AccessInOutVO(index, new ArrayList<>());
        }
        final List<TypeNumVO> typeNumList = new ArrayList<>(personRecordTypeList.size());
        if (CollUtil.isEmpty(recordList) || ObjUtil.isNotNull(timeHandleMapper)) {
            for (SysDictItem sysDictItem : personRecordTypeList) {
                typeNumList.add(new TypeNumVO(sysDictItem.getCode(), sysDictItem.getName(), 0));
            }
            return new AccessInOutVO(index, typeNumList);
        }
        for (SysDictItem sysDictItem : personRecordTypeList) {
            final long count = recordList.parallelStream()
                    .filter(e -> ObjUtil.isNotNull(e.getInOutTime()) && ObjUtil.isNotNull(e.getPersonType()))
                    .filter(e -> timeHandleMapper.apply(e.getInOutTime()).equals(index) && e.getPersonType().toString().equals(sysDictItem.getCode())).count();
            typeNumList.add(new TypeNumVO(sysDictItem.getCode(), sysDictItem.getName(), Long.valueOf(count).intValue()));
        }
        return new AccessInOutVO(index, typeNumList);
    }


    private <T> LambdaQueryWrapper<T> buildQueryWrapper(SFunction<T, Date> mapper, Date beginTime, Date endTime) {
        return Wrappers.<T>lambdaQuery().gt(mapper, beginTime).lt(mapper, endTime);
    }

    /**
     * 处理时间
     *
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @param mapper    函数，格式化时间
     * @param calendar  类型 如: Calendar.DAY_OF_YEAR, Calendar.HOUR_OF_DAY 等
     * @param step      步长
     * @return 结果
     */
    private List<String> handlerDateTime(Date beginTime, Date endTime, Function<Date, String> mapper, Integer calendar, Integer step) {
        final List<String> list = new ArrayList<>();
        final Calendar instance = Calendar.getInstance();
        instance.setTime(beginTime);
        while (instance.getTimeInMillis() < endTime.getTime()) {
            final String apply = mapper.apply(instance.getTime());
            list.add(apply);
            instance.add(calendar, step);
        }
        return list;
    }
}
